﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using Microsoft.Forums.SyncService;
using Microsoft.Forums.OfflineStore;
using System.Data.SqlClient;
using System.Reflection;
using Microsoft.Forums.Tests.HelperTools;
using Microsoft.Forums.Tests.HelperTools.Stubs;
using Microsoft.Forums.ForumsProvider;

namespace Microsoft.Forums.Tests.SyncService
{
    /// <summary>
    /// Tests for the SynchronizationController
    /// </summary>
    [TestFixture]
    public class SynchronizationControllerTest
    {
        /// <summary>
        /// Initialized the stub provider and stub connection.
        /// Connect to the database and creates a transaction for each test
        /// </summary>
        [TestFixtureSetUp]
        public void Setup()
        {
            DatabaseHelper.SetupDatabase();
        }

        /// <summary>
        /// After test is complete remove all data we added from the database
        /// 
        /// Ideally, we would just be able to recreate the database but there is an odd 
        /// file locked issue that happens intermittently
        /// </summary
        [TearDown]
        public void Teardown()
        {
            DatabaseHelper.ClearDatabase();
        }



        /// <summary>
        /// Sets up the synchronization controller with the deafult service provider 
        /// </summary>
        private SynchronizationController CreateSyncController()
        {
            var providerStub = GetNewForumsProviderStub();

            return new SynchronizationController(DatabaseHelper.ConnectionString,
                                                            new List<IForumServiceProvider> { providerStub });
        }

        /// <summary>
        /// Sets up the synchronization controller with the supplied service provider 
        /// </summary>
        private SynchronizationController CreateSyncController(IForumServiceProvider provider)
        {

            return new SynchronizationController(DatabaseHelper.ConnectionString,
                                                            new List<IForumServiceProvider> { provider });
        }

        /// <summary>
        /// Sets up the synchronization controller with the supplied service providers
        /// </summary>
        private SynchronizationController CreateSyncController(IList<IForumServiceProvider> providers)
        {

            return new SynchronizationController(DatabaseHelper.ConnectionString,
                                                            providers);

        }

        /// <summary>
        /// Gets the new forums provider stub.
        /// </summary>
        /// <returns>A new forums provider stub</returns>
        private ForumsProviderStub GetNewForumsProviderStub()
        {
            var providerStub = new ForumsProviderStub();
            providerStub.ProviderName_Result = "StubProvider";
            providerStub.ProviderId_Result = Guid.NewGuid();

            return providerStub;
        }


        #region Constructor Tests
        /// <summary>
        /// Ensures the constructor will add the forum providers information
        /// to the database
        /// </summary>
        [Test]
        public void EnsureConstructorAddsServiceProvidersToDatabase()
        {
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();

            ForumsProviderStub providerStub = new ForumsProviderStub();
            providerStub.ProviderId_Result = Guid.NewGuid();
            providerStub.ProviderName_Result = "StubProvider";

            CreateSyncController(providerStub);

            Assert.IsTrue(db.ForumServiceProviders.Any(p => p.Id == providerStub.ProviderId));

        }

        /// <summary>
        /// Ensures the constructor will detect if service provider is already in database
        /// and doesn't try to add it again which would cause a primary key exception exception
        /// </summary>
        [Test]
        public void EnsureConstructorDoesNotTryToAddExistingProvidersToDatabase()
        {
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();

            // This will load forumService providers listed in the xml file
            // into the database
            ForumDataCreator dataCreator = new ForumDataCreator(db);
            dataCreator.Load("LocalData1.xml");

            // Create sync controller using same list of providers already in the database
            CreateSyncController(dataCreator.Providers);

            foreach (var prov in dataCreator.Providers)
            {
                Assert.IsTrue(db.ForumServiceProviders.Any(p => p.Id == prov.ProviderId));
            }
            Assert.IsTrue(db.ForumServiceProviders.Count() == dataCreator.Providers.Count);

        }

        #endregion


        #region GetAvailableForums Tests
        /// <summary>
        /// Test for exception given an empty brand
        /// </summary>
        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void GetAvailableForumsThrowExceptionIfBrandIsEmpty()
        {
            SynchronizationController syncController = CreateSyncController();

            syncController.GetAvailableForums("", 1033);
        }

        /// <summary>
        /// Test for ArgumentException given a locale less than or equal to zero
        /// </summary>
        [Test]
        [ExpectedException(typeof(ArgumentException))]
        public void GetAvailableForumsThrowExceptionIfLocaleNoteGreaterThanZero()
        {
            SynchronizationController syncController = CreateSyncController();

            syncController.GetAvailableForums("doesntMatter", 0);
        }

        /// <summary>
        /// Ensure we get back all the forums we set
        /// </summary>
        [Test]
        public void GetAvailableForumsReturnsAllForums()
        {

            ForumsProviderStub providerStub = new ForumsProviderStub();
            providerStub.ProviderName_Result = "StubProvider";
            providerStub.ProviderId_Result = Guid.NewGuid();

            Forum f1 = new Forum { Id = Guid.NewGuid(), Name = "Forum1" };
            providerStub.GetAvailableForums_Result = new List<Forum> { f1 };
            SynchronizationController syncController = CreateSyncController(providerStub);

            // It doesnt matter what brand or locale we are passing in since we arent testing that
            // we just want to ensure what we set above gets returned
            var result = syncController.GetAvailableForums("doesntMatter", 1033);

            Assert.AreEqual(result.Count, 1);
            Assert.IsTrue(result.Any(f => f.Id == f1.Id));
        }

        #endregion


        /// <summary>
        /// Make sure get supported brands returns the brands we set
        /// </summary>
        [Test]
        public void GetSupportedBrandsReturnsAllBrands()
        {
            var providerStub = new ForumsProviderStub();
            providerStub.ProviderName_Result = "StubProvider";
            providerStub.ProviderId_Result = Guid.NewGuid();
            SynchronizationController syncController = CreateSyncController(providerStub);

            providerStub.GetSupportedBrands_Result = new List<string> { "Msdn", "Microsoft" };

            var result = syncController.SupportedBrands;

            Assert.AreEqual(2, result.Count);
            Assert.IsTrue(result.Contains("Msdn"));
            Assert.IsTrue(result.Contains("Microsoft"));
        }

        /// <summary>
        /// Make sure get supported locales returns the locales we set
        /// </summary>
        [Test]
        public void GetSupportedLocalesReturnsAllLocales()
        {
            var providerStub = new ForumsProviderStub();
            providerStub.ProviderName_Result = "StubProvider";
            providerStub.ProviderId_Result = Guid.NewGuid();
            providerStub.GetSupportedLocales_Result = new List<KeyValuePair<int, string>> 
                                                        { new KeyValuePair<int,string>(1033,"en-us"),
                                                          new KeyValuePair<int,string>(7,"de")};
            SynchronizationController syncController = CreateSyncController(providerStub);

            var result = syncController.SupportedLocales;

            Assert.AreEqual(2, result.Count);
            Assert.IsTrue(result.Contains(new KeyValuePair<int, string>(1033, "en-us")));
            Assert.IsTrue(result.Contains(new KeyValuePair<int, string>(7, "de")));
        }



        #region SubscribeToForum Tests

        /// <summary>
        /// Ensure we get a ArgumentNullException if we pass a null Forum object
        /// </summary>
        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void SubscribeToForumThrowsArgumentNullExceptionIfForumIsNull()
        {
            var providerStub = new ForumsProviderStub();
            providerStub.ProviderName_Result = "StubProvider";
            providerStub.ProviderId_Result = Guid.NewGuid();
            SynchronizationController syncController = CreateSyncController(providerStub);
            var property = new ForumPropertySet() { InitialSyncAge = 10, SyncFrequency = 10 };

            syncController.SubscribeToForum(null, property);
        }


        /// <summary>
        /// Ensure we get a ArgumentNullException if we pass a null ForumProperty object
        /// </summary>
        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void SubscribeToForumThrowsArgumentNullExceptionIfForumPropertyIsNull()
        {
            var providerStub = new ForumsProviderStub();
            providerStub.ProviderName_Result = "StubProvider";
            providerStub.ProviderId_Result = Guid.NewGuid();
            SynchronizationController syncController = CreateSyncController(providerStub);
            var forum = ForumObjectHelper.MakeNewForum("Forum1", providerStub.ProviderId_Result);

            syncController.SubscribeToForum(forum, null);
        }


        /// <summary>
        /// Test to see that a call to subscribeForum adds the forum to the database
        /// </summary>
        [Test]
        public void SubscribeToForumAddsForumToDatabase()
        {
            var providerStub = new ForumsProviderStub();
            providerStub.ProviderName_Result = "StubProvider";
            providerStub.ProviderId_Result = Guid.NewGuid();
            SynchronizationController syncController = CreateSyncController(providerStub);

            var forum = ForumObjectHelper.MakeNewForum("Forum1", providerStub.ProviderId_Result);
            var property = new ForumPropertySet() { InitialSyncAge = 10, SyncFrequency = 10 };

            syncController.SubscribeToForum(forum, property);

            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            Assert.IsTrue(db.Forums.Any(f => f.Id == forum.Id));
            Assert.IsTrue(db.ForumPropertySets.Any(f => f.ForumId == forum.Id));

        }

        /// <summary>
        /// Test to see if subscribing to a forum which is already in the database doesnt throw an error
        /// </summary>
        [Test]
        public void SubscribeToForumDoesNotAddAlreadySubscribedForum()
        {
            var providerStub = new ForumsProviderStub();
            providerStub.ProviderName_Result = "StubProvider";
            providerStub.ProviderId_Result = Guid.NewGuid();
            SynchronizationController syncController = CreateSyncController(providerStub);
            var forum = ForumObjectHelper.MakeNewForum("Forum1", providerStub.ProviderId_Result);
            var property = new ForumPropertySet() { InitialSyncAge = 10, SyncFrequency = 10 };
            syncController.SubscribeToForum(forum, property);

            syncController.SubscribeToForum(forum, property);

            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            Assert.IsTrue(db.Forums.Any(f => f.Id == forum.Id));
            Assert.IsTrue(db.ForumPropertySets.Any(f => f.ForumId == forum.Id));
        }

        #endregion




        #region UnsubscribeFromForum Tests


        /// <summary>
        /// Test to see that a call to unsubscribe forum removes the forum (and all attached items) from the database
        /// </summary>
        [Test]
        public void UnsubscribeFromForumRemovesForumToDatabase()
        {
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            ForumDataCreator dataCreator = new ForumDataCreator(db);
            dataCreator.Load("LocalData1.xml");
            var providerStub = dataCreator.ProviderStubs[0];
            SynchronizationController syncController = CreateSyncController(providerStub);
            Forum forum = db.Forums.Single(f => f.Name == "Forum1");

            syncController.UnsubscribeFromForum(forum.Id);
            db.Refresh(System.Data.Linq.RefreshMode.OverwriteCurrentValues);

            Assert.IsTrue(!db.Forums.Any(f => f.Id == forum.Id));
            Assert.IsTrue(!db.ForumPropertySets.Any(f => f.ForumId == forum.Id));
        }

        #endregion


        #region SynchronizeForum tests

        /// <summary>
        /// Ensure an exception is thrown if forum is not in database
        /// </summary>
        [Test]
        [ExpectedException(typeof(ForumNotInDatabaseException))]
        public void SynchronizeForumThrowsexceptionIfForumIsNotInDatabase()
        {
            SynchronizationController syncController = CreateSyncController();

            syncController.SynchronizeForum(Guid.NewGuid());

        }

        /// <summary>
        /// Ensure an exception is thrown if no forum service provider is found
        /// </summary>
        [Test]
        [ExpectedException(typeof(SynchronizationException))]
        public void SynchronizeForumThrowsexceptionIfNoServiceProviderIsFound()
        {
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            ForumDataCreator dataCreator = new ForumDataCreator(db);
            dataCreator.Load("LocalData1.xml");
            SynchronizationController syncController = CreateSyncController();
            Forum dbForum = db.Forums.Single(f => f.Name == "Forum1");

            syncController.SynchronizeForum(dbForum.Id);

        }

        /// <summary>
        /// Test that synchronizing a forum that has an update but has no new threads or new posts 
        /// correctly synchronizes just the forum
        /// </summary>
        [Test]
        public void SynchronizeForumUpdatesForumsAndForumPropertySetIfModified()
        {
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();

            ForumDataCreator dataCreator = new ForumDataCreator(db);
            dataCreator.Load("LocalData1.xml");
            var providerStub = dataCreator.ProviderStubs[0];
            var beforeSync = DateTime.Now.ToUniversalTime();
            SynchronizationController syncController = CreateSyncController(providerStub);

            var dbForum = db.Forums.Single(f => f.Name == "Forum1");
            var previousSyncDate = dbForum.ForumPropertySet.LastSyncedOn;
            var newForum = new Forum();
            newForum.Id = dbForum.Id;
            newForum.Name = newForum.DisplayName = "ChangedName";
            newForum.ForumServiceProviderId = providerStub.ProviderId;
            newForum.ModifiedOn = DateTime.Now.AddDays(1);
            providerStub.GetForum_Result = newForum;

            var summary = syncController.SynchronizeForum(dbForum.Id);
            db.Refresh(System.Data.Linq.RefreshMode.OverwriteCurrentValues, dbForum, dbForum.ForumPropertySet);

            Assert.IsTrue(dbForum.Name == "ChangedName");
            Assert.IsTrue(dbForum.ForumPropertySet.LastSyncedOn >= beforeSync);
            // check that no thread of this forum has been updated
            Assert.IsFalse(dbForum.Threads.Any(t => t.ThreadPropertySet.LastSyncedOn >= previousSyncDate));

            // Make sure the summary object is correct
            Assert.AreEqual(dbForum.Id, summary.ForumId);
            Assert.AreEqual(0, summary.ThreadSummaries.Count);
        }


        /// <summary>
        /// Test that synchronizing a forum that has a updated thread that doesnt exist in our database, 
        /// will add that thread to the database and also add and set up its ThreadPropertySet
        /// </summary>
        [Test]
        public void SynchronizeForumAddsNewThreadAndThreadPropertySetToDatabase()
        {
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            ForumDataCreator dataCreator = new ForumDataCreator(db);
            dataCreator.Load("LocalData1.xml");
            var providerStub = dataCreator.ProviderStubs[0];
            var beforeSync = DateTime.Now.ToUniversalTime();
            SynchronizationController syncController = CreateSyncController(providerStub);

            var dbForum = db.Forums.Single(f => f.Name == "Forum1");
            var originalThreadCount = db.Threads.Count();

            // Make copy of original forum since serviceProviders return detached items
            var copyForum = ForumObjectHelper.Duplicate(dbForum);
            copyForum.ModifiedOn = DateTime.Now.AddDays(1);
            providerStub.GetForum_Result = copyForum; // return same forum when getForum is called

            // set up thread to be returned by provider
            Thread newThread = ForumObjectHelper.MakeNewThread("SomeTopic", copyForum.Id);
            newThread.CreatedOn = DateTime.Now.AddDays(-1);
            providerStub.GetUpdatedThreads_Result = new List<Thread> { newThread }; // return newThread when get GetUpdatedThreads is called

            var summary = syncController.SynchronizeForum(dbForum.Id);

            Assert.AreEqual(originalThreadCount + 1, db.Threads.Count());
            Assert.IsTrue(db.Threads.Any(t => t.Id == newThread.Id && t.Topic == newThread.Topic));
            Assert.IsTrue(db.ThreadPropertySets.Any(tp => tp.LastSyncedOn >= beforeSync && tp.ThreadId == newThread.Id));

            // Make sure the summary object is correct
            Assert.AreEqual(dbForum.Id, summary.ForumId);
            Assert.AreEqual(1, summary.ThreadSummaries.Count);
            Assert.AreEqual(newThread.Id, summary.ThreadSummaries[0].ThreadId);
            Assert.AreEqual(SynchronizeType.Add, summary.ThreadSummaries[0].SynchronizeType);
            Assert.AreEqual(0, summary.ThreadSummaries[0].PostSummaries.Count);

        }

        /// <summary>
        /// Test that synchronizing a forum that has an updated thread that exists in our database, 
        /// will update that thread in the database 
        /// </summary>
        [Test]
        public void SynchronizeForumUpdatesExistingThreadInDatabase()
        {
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();

            ForumDataCreator dataCreator = new ForumDataCreator(db);
            dataCreator.Load("LocalData1.xml");
            var providerStub = dataCreator.ProviderStubs[0];
            var beforeSync = DateTime.Now.ToUniversalTime();
            SynchronizationController syncController = CreateSyncController(providerStub);

            var dbForum = db.Forums.Single(f => f.Name == "Forum1");

            // Make copy of original forum since serviceProviders return detached items
            var copyForum = ForumObjectHelper.Duplicate(dbForum);
            copyForum.ModifiedOn = DateTime.Now.AddDays(1);
            providerStub.GetForum_Result = copyForum; // return same forum when getForum is called

            // set up thread to be returned by provider
            var dbThread = db.Threads.Single(t => t.Topic == "Thread1"); // existing thread
            var updatedThread = ForumObjectHelper.Duplicate(dbThread);
            updatedThread.Topic = "NewTopic";
            providerStub.GetUpdatedThreads_Result = new List<Thread> { updatedThread }; // return updatedThread when get GetUpdatedThreads is called

            var summary = syncController.SynchronizeForum(dbForum.Id);

            Assert.IsFalse(db.Threads.Any(t => t.Topic == "Thread1")); // old topic gone
            Assert.IsTrue(db.Threads.Any(t => t.Topic == "NewTopic")); // new topic there
            Assert.IsTrue(db.ThreadPropertySets.Any(tp => tp.LastSyncedOn >= beforeSync && tp.ThreadId == updatedThread.Id));

            // Make sure the summary object is correct
            Assert.AreEqual(dbForum.Id, summary.ForumId);
            Assert.AreEqual(1, summary.ThreadSummaries.Count);
            Assert.AreEqual(updatedThread.Id, summary.ThreadSummaries[0].ThreadId);
            Assert.AreEqual(SynchronizeType.Update, summary.ThreadSummaries[0].SynchronizeType);
            Assert.AreEqual(0, summary.ThreadSummaries[0].PostSummaries.Count);


        }

        /// <summary>
        /// Test that synchronizing a forum that has a updated post that doesnt exist in our database, 
        /// will add that post to the database and also add and set up its PostPropertySet
        /// </summary>
        [Test]
        public void SynchronizeForumAddsNewPostAndPostPropertySetToDatabase()
        {
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();

            ForumDataCreator dataCreator = new ForumDataCreator(db);
            dataCreator.Load("LocalData1.xml");
            var providerStub = dataCreator.ProviderStubs[0];
            var beforeSync = DateTime.Now.ToUniversalTime();
            SynchronizationController syncController = CreateSyncController(providerStub);

            var originalPostCount = db.Posts.Count();
            var dbForum = db.Forums.Single(f => f.Name == "Forum1");

            // Make copy of original forum since serviceProviders return detached items
            var copyForum = ForumObjectHelper.Duplicate(dbForum);
            copyForum.ModifiedOn = DateTime.Now.AddDays(1);
            providerStub.GetForum_Result = copyForum; // return same forum when getForum is called


            // set up thread to be returned by provider
            var dbThread = db.Threads.Single(t => t.Topic == "Thread1"); // existing thread
            var updatedThread = ForumObjectHelper.Duplicate(dbThread);
            providerStub.GetUpdatedThreads_Result = new List<Thread> { updatedThread }; // return updatedThread when get GetUpdatedThreads is called

            // set up new post to be returned by provider
            Post newPost = ForumObjectHelper.MakeNewPost(dbThread.Topic, copyForum.Id, dbThread.Id, null);
            var createdById = newPost.CreatedBy.Id;
            providerStub.GetUpdatedPosts_Result = new List<Post> { newPost };

            var summary = syncController.SynchronizeForum(dbForum.Id);


            Assert.AreEqual(originalPostCount + 1, db.Posts.Count());
            Assert.IsTrue(db.Posts.Any(p => p.Id == newPost.Id && p.ThreadId == newPost.ThreadId && p.Subject == newPost.Subject && p.ThreadModifiedOn == updatedThread.ModifiedOn));
            Assert.IsTrue(db.PostPropertySets.Any(pp => pp.PostId == newPost.Id && !pp.IsRead));
            Assert.IsTrue(db.ThreadPropertySets.Any(tp => tp.LastSyncedOn >= beforeSync && tp.ThreadId == updatedThread.Id));
            Assert.IsTrue(db.Users.Any(u => u.Id == createdById));

            // Make sure the summary object is correct
            Assert.AreEqual(dbForum.Id, summary.ForumId);
            Assert.AreEqual(1, summary.ThreadSummaries.Count);
            Assert.AreEqual(updatedThread.Id, summary.ThreadSummaries[0].ThreadId);
            Assert.AreEqual(SynchronizeType.Update, summary.ThreadSummaries[0].SynchronizeType);
            Assert.AreEqual(1, summary.ThreadSummaries[0].PostSummaries.Count);
            Assert.AreEqual(newPost.Id, summary.ThreadSummaries[0].PostSummaries[0].PostId);
            Assert.AreEqual(SynchronizeType.Add, summary.ThreadSummaries[0].PostSummaries[0].SynchronizeType);
        }

        /// <summary>
        /// Test that synchronizing a forum that has a updated post that does exist in our database, 
        /// will update that post to the database and also update its PostPropertySet
        /// </summary>
        [Test]
        public void SynchronizeForumUpdatesExistingPostInDatabase()
        {
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            ForumDataCreator dataCreator = new ForumDataCreator(db);
            dataCreator.Load("LocalData1.xml");
            var providerStub = dataCreator.ProviderStubs[0];
            DateTime beforeSync = DateTime.Now.ToUniversalTime();
            SynchronizationController syncController = CreateSyncController(providerStub);
            int originalPostCount = db.Posts.Count();
            Forum dbForum = db.Forums.Single(f => f.Name == "Forum1");

            // Make copy of original forum since serviceProviders return detached items
            var copyForum = ForumObjectHelper.Duplicate(dbForum);
            copyForum.ModifiedOn = DateTime.Now.AddDays(1);
            providerStub.GetForum_Result = copyForum; // return same forum when getForum is called

            // set up thread to be returned by provider
            var dbThread = db.Threads.Single(t => t.Topic == "Thread1"); // existing thread
            var updatedThread = ForumObjectHelper.Duplicate(dbThread);
            providerStub.GetUpdatedThreads_Result = new List<Thread> { updatedThread }; // return updatedThread when get GetUpdatedThreads is called

            // set up new post to be returned by provider
            var dbPost = db.Posts.Single(p => p.Subject == "Post1");
            var updatedPost = ForumObjectHelper.Duplicate(dbPost);
            var postCreatedBy = ForumObjectHelper.Duplicate(dbPost.CreatedBy);
            updatedPost.Subject = "NewSubject";
            updatedPost.CreatedBy = postCreatedBy;

            providerStub.GetUpdatedPosts_Result = new List<Post> { updatedPost };

            var summary = syncController.SynchronizeForum(dbForum.Id);

            Assert.IsTrue(
                db.Posts.Any(p => p.Id == updatedPost.Id &&
                p.Subject == updatedPost.Subject));
            Assert.AreEqual(originalPostCount, db.Posts.Count());
            Assert.IsTrue(db.PostPropertySets.Any(pp => pp.PostId == updatedPost.Id));
            Assert.IsTrue(db.ThreadPropertySets.Any(tp => tp.LastSyncedOn >= beforeSync && tp.ThreadId == updatedThread.Id));

            // Make sure the summary object is correct
            Assert.AreEqual(dbForum.Id, summary.ForumId);
            Assert.AreEqual(1, summary.ThreadSummaries.Count);
            Assert.AreEqual(updatedThread.Id, summary.ThreadSummaries[0].ThreadId);
            Assert.AreEqual(SynchronizeType.Update, summary.ThreadSummaries[0].SynchronizeType);
            Assert.AreEqual(1, summary.ThreadSummaries[0].PostSummaries.Count);
            Assert.AreEqual(updatedPost.Id, summary.ThreadSummaries[0].PostSummaries[0].PostId);
            Assert.AreEqual(SynchronizeType.Update, summary.ThreadSummaries[0].PostSummaries[0].SynchronizeType);
        }

        #endregion


        #region ProcessPendingActionQueue Tests

        /// <summary>
        /// Process Pending Action Queue Throws If PostId Is Null When Marking As Answer
        /// </summary>
        [Test]
        [ExpectedException(typeof(SynchronizationDatabaseException))]
        public void ProcessPendingActionQueueThrowsIfPostIdIsNullWhenMarkingAsAnswer()
        {
            ForumsProviderStub providerStub = GetNewForumsProviderStub();
            SynchronizationController syncController = CreateSyncController(providerStub);
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            PendingAction action = ForumObjectHelper.MakePendingAction(ActionStatus.Pending, ActionType.MarkAsAnswer, providerStub.ProviderId);
            action.PostId = null;
            db.PendingActions.InsertOnSubmit(action);
            db.SubmitChanges();

            syncController.ProcessPendingActionQueue();
        }

        /// <summary>
        /// Process Pending Action Queue Throws If PostId Is Null When Unmarking As Answer
        /// </summary>
        [Test]
        [ExpectedException(typeof(SynchronizationDatabaseException))]
        public void ProcessPendingActionQueueThrowsIfPostIdIsNullWhenUnmarkingAsAnswer()
        {
            ForumsProviderStub providerStub = GetNewForumsProviderStub();
            SynchronizationController syncController = CreateSyncController(providerStub);
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            PendingAction action = ForumObjectHelper.MakePendingAction(ActionStatus.Pending, ActionType.UnmarkAsAnswer, providerStub.ProviderId);
            action.PostId = null;
            db.PendingActions.InsertOnSubmit(action);
            db.SubmitChanges();

            syncController.ProcessPendingActionQueue();
        }

        /// <summary>
        /// Process Pending Action Queue Throws If PostId Is Null When Replying
        /// </summary>
        [Test]
        [ExpectedException(typeof(SynchronizationDatabaseException))]
        public void ProcessPendingActionQueueThrowsIfPostIdIsNullWhenReplying()
        {
            ForumsProviderStub providerStub = GetNewForumsProviderStub();
            SynchronizationController syncController = CreateSyncController(providerStub);
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            PendingAction action = ForumObjectHelper.MakePendingAction(ActionStatus.Pending, ActionType.Reply, providerStub.ProviderId);
            action.PostId = null;
            action.ThreadId = Guid.NewGuid();
            db.PendingActions.InsertOnSubmit(action);
            db.SubmitChanges();

            syncController.ProcessPendingActionQueue();
        }

        /// <summary>
        /// Process Pending Action Queue Throws If ThreadId Is Null When Replying
        /// </summary>
        [Test]
        [ExpectedException(typeof(SynchronizationDatabaseException))]
        public void ProcessPendingActionQueueThrowsIfThreadIdIsNullWhenReplying()
        {
            ForumsProviderStub providerStub = GetNewForumsProviderStub();
            SynchronizationController syncController = CreateSyncController(providerStub);
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            PendingAction action = ForumObjectHelper.MakePendingAction(ActionStatus.Pending, ActionType.Reply, providerStub.ProviderId);
            action.PostId = Guid.NewGuid();
            action.ThreadId = null;
            db.PendingActions.InsertOnSubmit(action);
            db.SubmitChanges();

            syncController.ProcessPendingActionQueue();
        }

        /// <summary>
        /// Process Pending Action Queue Does Not Process Failed Actions And Removes Succeded Actions From Queue
        /// </summary>
        [Test]
        public void ProcessPendingActionQueueDoesNotProcessFailedActionsAndRemovesSuccededActionsFromQueue()
        {
            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            ForumDataCreator dataCreator = new ForumDataCreator(db);
            dataCreator.Load("PendingActions1.xml");
            SynchronizationController syncController = CreateSyncController(dataCreator.ProviderStubs[0]);
            int actionCount = db.PendingActions.Count();
            syncController.ProcessPendingActionQueue();
            int actionsLeft = db.PendingActions.Count();

            Assert.AreEqual(10, actionCount);
            Assert.AreEqual(5, actionsLeft);
        }


        /// <summary>
        /// Process Pending Action Queue Marks Failed Replies As Failed
        /// </summary>
        [Test]
        public void ProcessPendingActionQueueMarksFailedRepliesAsFailed()
        {

            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            ForumDataCreator dataCreator = new ForumDataCreator(db);
            dataCreator.Load("PendingActions1.xml");
            SynchronizationController syncController = CreateSyncController(dataCreator.ProviderStubs[0]);
            dataCreator.ProviderStubs[0].FailAtCreatingReply = true;

            int actionCount = db.PendingActions.Count();
            syncController.ProcessPendingActionQueue();
            int actionsLeft = db.PendingActions.Count();

            Assert.AreEqual(10, actionCount);
            Assert.AreEqual(6, actionsLeft);
            Assert.AreEqual(2, db.PendingActions.Count(p => p.ActionType == (int)ActionType.Reply && p.ActionStatus == (int)ActionStatus.Failed));
        }

        /// <summary>
        /// Process Pending Action Queue Marks Failed Threds As Failed
        /// </summary>
        [Test]
        public void ProcessPendingActionQueueMarksFailedThreadsAsFailed()
        {

            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            ForumDataCreator dataCreator = new ForumDataCreator(db);
            dataCreator.Load("PendingActions1.xml");
            SynchronizationController syncController = CreateSyncController(dataCreator.ProviderStubs[0]);
            dataCreator.ProviderStubs[0].FailAtCreatingCommentThread = true;
            dataCreator.ProviderStubs[0].FailAtCreatingQuestionThread = true;

            int actionCount = db.PendingActions.Count();
            syncController.ProcessPendingActionQueue();
            int actionsLeft = db.PendingActions.Count();

            Assert.AreEqual(10, actionCount);
            Assert.AreEqual(7, actionsLeft);
            Assert.AreEqual(2, db.PendingActions.Count(p => p.ActionType == (int)ActionType.NewThread && p.ThreadType == (int)ThreadType.Comment &&
                                                            p.ActionStatus == (int)ActionStatus.Failed));
            Assert.AreEqual(2, db.PendingActions.Count(p => p.ActionType == (int)ActionType.NewThread && p.ThreadType == (int)ThreadType.Question &&
                                                            p.ActionStatus == (int)ActionStatus.Failed));
        }

        /// <summary>
        /// Process Pending Action Queue Marks Failed MarkAsAnswer As Failed
        /// </summary>
        [Test]
        public void ProcessPendingActionQueueMarksFailedMarkAsAnswerAsFailed()
        {

            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            ForumDataCreator dataCreator = new ForumDataCreator(db);
            dataCreator.Load("PendingActions1.xml");
            SynchronizationController syncController = CreateSyncController(dataCreator.ProviderStubs[0]);
            dataCreator.ProviderStubs[0].FailAtMarkingAsAnswer = true;

            int actionCount = db.PendingActions.Count();
            syncController.ProcessPendingActionQueue();
            int actionsLeft = db.PendingActions.Count();

            Assert.AreEqual(10, actionCount);
            Assert.AreEqual(6, actionsLeft);
            Assert.AreEqual(2, db.PendingActions.Count(p => p.ActionType == (int)ActionType.MarkAsAnswer && p.ActionStatus == (int)ActionStatus.Failed));
        }

        /// <summary>
        /// Process Pending Action Queue Marks Failed UnmarkAsAnswer As Failed
        /// </summary>
        [Test]
        public void ProcessPendingActionQueueMarksFailedUnmarkAsAnswerAsFailed()
        {

            ForumsOfflineStore db = DatabaseHelper.GetNewDataContext();
            ForumDataCreator dataCreator = new ForumDataCreator(db);
            dataCreator.Load("PendingActions1.xml");
            SynchronizationController syncController = CreateSyncController(dataCreator.ProviderStubs[0]);
            dataCreator.ProviderStubs[0].FailAtUnmarkingAsAnswer = true;

            int actionCount = db.PendingActions.Count();
            syncController.ProcessPendingActionQueue();
            int actionsLeft = db.PendingActions.Count();

            Assert.AreEqual(10, actionCount);
            Assert.AreEqual(6, actionsLeft);
            Assert.AreEqual(2, db.PendingActions.Count(p => p.ActionType == (int)ActionType.UnmarkAsAnswer && p.ActionStatus == (int)ActionStatus.Failed));
        }
        #endregion

    }
}
